package com.xique.door.project.xyl.websocket;

import com.xique.common.core.utils.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
import org.springframework.web.socket.sockjs.transport.session.WebSocketServerSockJsSession;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Administrator
 */
@Component
public class XylWebSocketHandler extends AbstractWebSocketHandler {

    /**
     * 存储sessionId和webSocketSession
     * 需要注意的是，webSocketSession没有提供无参构造，不能进行序列化，也就不能通过redis存储
     * 在分布式系统中，要想别的办法实现webSocketSession共享
     */
    private static Map<String, WebSocketSession> sessionMap = new ConcurrentHashMap<>();
    private static Map<String, List<String>> projectMap = new ConcurrentHashMap<>();

    /**
     * 获取sessionId
     */
    private String getSessionId(WebSocketSession session) {
        if (session instanceof WebSocketServerSockJsSession) {
            // sock js 连接
            try {
                return  ((WebSocketSession) FieldUtils.readField(session, "webSocketSession", true)).getId();
            } catch (IllegalAccessException e) {
                throw new RuntimeException("get sessionId error");
            }
        }
        return session.getId();
    }

    /**
     * webSocket连接创建后调用
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        String project = String.valueOf(session.getAttributes().get("project"));
        List<String> sessions = projectMap.get(project);
        if (StringUtils.isEmpty(sessions)) {
            sessions = new ArrayList<>();
        }
        String sessionId = getSessionId(session);
        sessions.add(sessionId);
        projectMap.put(project, sessions);
        sessionMap.put(sessionId, session);
    }

    /**
     * 接收到消息会调用
     */
    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        if (message instanceof TextMessage) {
            String project = String.valueOf(session.getAttributes().get("project"));
            TextMessage textMessage = (TextMessage) message;
            sendMessage(project, textMessage.getPayload());
            System.out.println("WebSocket message type: " + message);
        } else if (message instanceof BinaryMessage) {

        } else if (message instanceof PongMessage) {

        } else {
            System.out.println("Unexpected WebSocket message type: " + message);
        }
    }

    /**
     * 连接出错会调用
     */
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) {
        String project = String.valueOf(session.getAttributes().get("project"));
        for (String sessionId : projectMap.get(project)) {
            if (getSessionId(session).equals(sessionId)) {
                projectMap.get(project).remove(sessionId);
                break;
            }
        }
        sessionMap.remove(getSessionId(session));

    }

    /**
     * 连接关闭会调用
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        String project = String.valueOf(session.getAttributes().get("project"));
        for (String sessionId : projectMap.get(project)) {
            if (getSessionId(session).equals(sessionId)) {
                projectMap.get(project).remove(sessionId);
                break;
            }
        }
        sessionMap.remove(getSessionId(session));
    }

    @Override
    public boolean supportsPartialMessages() {
        return false;
    }

    /**
     * 后端发送消息
     */
    public void sendMessage(String project, String message) {
        for (String sessionId : projectMap.get(project)) {
            WebSocketSession session = sessionMap.get(sessionId);
            try {
                session.sendMessage(new TextMessage(message));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
